ZYNQ学习之路10.DMA PS(PL330)基础 您所在的位置:网站首页 zynq pl uart ZYNQ学习之路10.DMA PS(PL330)基础

ZYNQ学习之路10.DMA PS(PL330)基础

#ZYNQ学习之路10.DMA PS(PL330)基础| 来源: 网络整理| 查看: 265

1. 什么是DMA

DMA是直接内存访问(Direct Memory Access),DMA引擎可以将数据从一个地方传输到另一个地方,在传输过程中不经过CPU的控制。最简单的DMA用法是将数据从内存的一个区域搬运到另一个区域。DMA也可以将外设的数据(如ADC)搬运到内存中,或者将内存数据搬运到外设中(如DAC)。

Zynq-7000系列器件PS端的DMA控制器采用ARM的IP核DMA-330(PL-330)实现。

开发环境 Windows 10 64位Vivado 2018.2XC7Z010-1-CLG400 1.1 结构特点

DMA控制器具有以下的特点:

8个独立的通道,4个可用于PL—PS间数据管理,每个通道有1024Byte的MFIFO;使用CPU_2x 时钟搬运数据,CPU_2x = (CPU frq/6)*2;执行自定义内存区域内的DMA指令运行DMA;AHB控制寄存器支持安全和非安全模式;每个通道内置4字Cache;可以访问SoC的以下映射物理地址:

DDR、OCM、PL、Linear QSPI Read、SMC和M_AXI_GP设备,访问设备的互联结构如图1所示。

图1 DMA PS结构示意图

 

 1.2 Zynq 访问互联结构图

从图1可以看出DMA控制器可以访问连接到Central Interconnect上的所有设备,并提供了四个通道的外设管理接口可用于控制PL的数据搬运。

 Zynq系列器件中DMA控制器采用ARM PL-330 IP和r1p1版,结构框图如图2所示。

图2 ZYNQ DMA控制器结构示意图

 

 如图2所示,DMA控制器由指令加速引擎,AXI Master数据接口,AXI APB寄存器访问接口以及可以连接到PL的外设请求接口,数据缓冲FIFO和控制及状态产生单元组成。

从图2可以看到,DMA PL330的设计思想是:DMA控制器通过DMA指令执行引擎执行自己的指令,并将执行状态通过APB总线和中断等形式反馈给CPU,达到数据搬运不占用CPU的目的。

DMA控制器共有八个通道,其中四个通道负责互联到Central Interconnectcun存储单元上的数据搬运;四个数据通道为外设请求接口,可用于PL AXI互联接口的数据访问管理。

每个DMA通道都执行自己的指令,拥有自己的独立线程,通道间互不影响。指令执行引擎有自己独立的Cache线。

2. 实例测试

首先构建AXI DMA例程使用的硬件环境,如图3所示,ZYNQ通过GP0端口读取Block RAM数据。

图3 Block RAM硬件结构  2.1 测试硬件完整性

首先使用SDK测试硬件的完成整性,编写如下代码测试BRAM读写情况。

#include #include "platform.h" #include "xil_printf.h" #include "xtime_l.h" #include "xparameters.h" void TC_BRAM(); #define RAM_W XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR #define RAM_R XPAR_AXI_BRAM_CTRL_1_S_AXI_BASEADDR int main() { init_platform(); TC_BRAM(); cleanup_platform(); return 0; } void TC_BRAM() { printf("test for block RAM\n"); XTime tb, te; double dt = 0.0; XTime_SetTime(0); for(int i=0; iBD.DstAddr; /* DMA successful */ /* compare the src and dst buffer */ for (Index = 0; Index < DMA_LENGTH; Index++) { if ((Src[Index] != Dst[Index]) || (Dst[Index] != DMA_LENGTH - Index)) { Status = -XST_FAILURE; } } Checked[Channel] = Status; }  3.2 修改DMA PS

修改DMA配置,使其将PL中的数据传输到内存中。

修改DMA的源地址:

DmaCmd.BD.SrcAddr = (u32) RAM_R;

 测量DMA传输16KB数据,时间约为180us,远远高于memcpy。

4. Linux DMA驱动 4.1 编程方法

配置DMA

void dma_init(u32 s, int size) { dma_cap_mask_t mask; //alloc 512B src memory and dst memory dma_src = s; printk(KERN_INFO "dma_src = 0x%x\n",src); //src = dma_alloc_coherent(NULL, MM_SIZE, &dma_src, GFP_KERNEL); dst = dma_alloc_coherent(NULL, size, &dma_dst, GFP_KERNEL); printk(KERN_INFO "dst = 0x%x, dma_dst = 0x%x\n",dst, dma_dst); dma_cap_zero(mask); dma_cap_set(DMA_MEMCPY, mask);//direction:memory to memory chan = dma_request_channel(mask,NULL,NULL); //request a dma channel printk(KERN_INFO "dma channel id = %d\n",chan->chan_id); flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; dev = chan->device; }

 释放DMA

void dma_del(void) { //free memory and dma channel dma_free_coherent(NULL, MM_SIZE, dst, &dma_dst); dma_release_channel(chan); }

 向DMA引擎发起一个传输请求

void dma_read(u32 dst,u32 src,int size) { //alloc a desc,and set dst_addr,src_addr,data_size. /*获取时间*/ do_gettimeofday(&tb); tx = dev->device_prep_dma_memcpy(chan, dst, src, size, flags); if (!tx){ printk(KERN_INFO "Failed to prepare DMA memcpy"); } tx->callback = dma_callback_func;//set call back function tx->callback_param = NULL; cookie = tx->tx_submit(tx); //submit the desc if (dma_submit_error(cookie)){ printk(KERN_INFO "Failed to do DMA tx_submit"); } dma_async_issue_pending(chan);//begin dma transfer }  4.2 实例代码

将Block RAM中的数据先使用ioremap映射的地址src,写入一些字符,然后使用DMA从Block RAM中传输16KB数据到分配的内存dst中。传输完成后调用dma_callback_func函数,在该函数中比较传输的数据和发送的数据是否相同,并测量DMA消耗的时间。

#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEVICE_NAME "dma_driver" #define ImageReadAddress0 0x40000000 volatile unsigned int *CaptureReadAddr0; struct timeval tb, te; #define MM_SIZE (1440*10) void dma_callback_func(void *dma_async_param); void dma_read(u32 dma_dst,u32 dma_src,int size); void dma_init(u32 s, int size); void dma_del(void); struct dma_chan *chan; //bus address dma_addr_t dma_src; dma_addr_t dma_dst; //virtual address char *src = NULL; char *dst = NULL ; struct dma_device *dev; struct dma_async_tx_descriptor *tx = NULL; enum dma_ctrl_flags flags; dma_cookie_t cookie; //When dma transfer finished,this function will be called. void dma_callback_func(void *dma_async_param) { int i=0; do_gettimeofday(&te); printk("DMA\n"); printk("T:%ld, %ld\n", tb.tv_sec, tb.tv_usec); printk("T2:%ld, %ld\n", te.tv_sec, te.tv_usec); printk(KERN_ALERT "time use:%ld, %ld\n", (te.tv_sec-tb.tv_sec), (te.tv_usec-tb.tv_usec)); printk("memcpy\n"); do_gettimeofday(&tb); memcpy(dst ,src, MM_SIZE); do_gettimeofday(&te); printk("T:%ld, %ld\n", tb.tv_sec, tb.tv_usec); printk("T2:%ld, %ld\n", te.tv_sec, te.tv_usec); printk(KERN_ALERT "time use:%ld, %ld\n", (te.tv_sec-tb.tv_sec), (te.tv_usec-tb.tv_usec)); printk("DMA transfer finished!\n\r"); for(i=0; idevice_prep_dma_memcpy(chan, dst, src, size, flags); if (!tx){ printk(KERN_INFO "Failed to prepare DMA memcpy"); } tx->callback = dma_callback_func;//set call back function tx->callback_param = NULL; cookie = tx->tx_submit(tx); //submit the desc if (dma_submit_error(cookie)){ printk(KERN_INFO "Failed to do DMA tx_submit"); } dma_async_issue_pending(chan);//begin dma transfer } void dma_init(u32 s, int size) { dma_cap_mask_t mask; //alloc 512B src memory and dst memory dma_src = s; printk(KERN_INFO "dma_src = 0x%x\n",src); //src = dma_alloc_coherent(NULL, MM_SIZE, &dma_src, GFP_KERNEL); dst = dma_alloc_coherent(NULL, size, &dma_dst, GFP_KERNEL); printk(KERN_INFO "dst = 0x%x, dma_dst = 0x%x\n",dst, dma_dst); dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask);//direction:memory to memory chan = dma_request_channel(mask,NULL,NULL); //request a dma channel printk(KERN_INFO "dma channel id = %d\n",chan->chan_id); flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; dev = chan->device; } void dma_del(void) { //free memory and dma channel dma_free_coherent(NULL, MM_SIZE, dst, &dma_dst); dma_release_channel(chan); } static int device_open(struct inode *inode, struct file *file) { return 0; } static int device_close(struct inode *indoe, struct file *file) { printk("device close\n"); return 0; } static ssize_t device_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos) { int ret = 0; dma_read(dma_dst, dma_src, MM_SIZE); return ret; } static struct file_operations device_fops = { .owner = THIS_MODULE, .open = device_open, .release = device_close, .read = device_read, }; static struct miscdevice MMAP_misc = { .minor = MISC_DYNAMIC_MINOR, .name = DEVICE_NAME, .fops = &device_fops, }; static int __init char_device_init( void ) { int ret=0; int i = 0; printk("init module\n"); ret = misc_register(&MMAP_misc); if(ret) { printk("Error:misc_register failed!\n"); return 0; } CaptureReadAddr0 = (volatile unsigned int*)ioremap(ImageReadAddress0, 1440*10); printk("init module\n"); dma_init(ImageReadAddress0, MM_SIZE); src = (char*)CaptureReadAddr0; for (i = 0; i < MM_SIZE; i++){ *(src + i) = (char)('a' + i%26); } return 0; } static void __exit char_device_exit( void ) { printk(KERN_ALERT"module exit\n"); misc_deregister(&MMAP_misc); iounmap(CaptureReadAddr0); dma_del(); } MODULE_LICENSE("GPL"); MODULE_AUTHOR("DMA_test"); module_init(char_device_init);//模块加载 module_exit(char_device_exit);//模块退出

 使用DMA搬运和memcpy搬运PL中的数据速度对比如下:

Z-turn# ./test Test for dma DMA T:34, 358179 T2:34, 358290 time use:0, 111 memcpy T:34, 364372 T2:34, 364796 time use:0, 424 DMA transfer finished! PASS

 DMA搬运消耗了111us,而memcpy需要使用424us,可见DMA速度远高于CPU对数据的搬运。

欢迎关注亦梦云烟的微信公众号: 亦梦智能计算

 



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有